package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestService;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author atguigu-mqx
 * @Description TODO
 * @Date 2023/7/13 14:29
 * @Version 1.0
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;


    /*
        在缓存中设置一个变量 num , 初始化值 0;
            获取到num值，
                如果不为空，则对其+1，并写入缓存.
                如何为空，则直接返回.
     */
    @Override
    public void testLock() {
        //  上锁：
        RLock lock = redissonClient.getLock("lock");
        lock.lock();
        lock.lock(10,TimeUnit.SECONDS);
        //  如果到了最大等待时间之后，这个线程是继续等待呢? 还剩执行完成?
        boolean result = false;
        try {
            result = lock.tryLock(100, 10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (result){
            //  执行业务逻辑
            try {
                //  获取num 数据
                String num = this.redisTemplate.opsForValue().get("num");
                //  判断
                if (StringUtils.isEmpty(num)){
                    return;
                }
                //  不为空。
                int number = Integer.parseInt(num);
                //  int i = 1/0;
                this.redisTemplate.opsForValue().set("num", String.valueOf(++number));
            } catch (NumberFormatException e) {
                throw new RuntimeException(e);
            } finally {
                //  解锁：
                lock.unlock();
            }
        }else {
            System.out.println("没有获取到锁....");
        }

    }

    @Override
    public String read() {
        //  声明一个读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //  上锁  10秒之后自动释放锁.
        rwlock.readLock().lock(10,TimeUnit.SECONDS);
        String msg = this.redisTemplate.opsForValue().get("msg");
        //  解锁
        //  rwlock.writeLock().unlock();
        //  返回读取的内容
        return msg;
    }

    @Override
    public String write() {
        //  声明一个读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //  10秒之后自动释放锁.
        rwlock.writeLock().lock(10,TimeUnit.SECONDS);
        String uuid = UUID.randomUUID().toString();
        this.redisTemplate.opsForValue().set("msg",uuid);
        //  解锁
        //  rwlock.writeLock().unlock();
        return uuid;
    }


//    @Override
//    public void testLock() {
//        //  每个线程执行到此，都应先执行上锁业务逻辑. setnx key value;
//        //  Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", "atguigu");
//        //  set lock ok ex 3 nx  3秒钟之后key失效！ 就意味着锁释放！ 时间是多少? 根据执行业务的时间决定 2
//        //        Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", "atguigu",3,TimeUnit.SECONDS);
//        String token = UUID.randomUUID().toString();
//        Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", token,3,TimeUnit.SECONDS);
//
//        if (result){
//            //  expire lock 5;
//            //  this.redisTemplate.expire("lock",5, TimeUnit.SECONDS);
//            //  获取num 数据
//            String num = this.redisTemplate.opsForValue().get("num");
//            //  判断
//            if (StringUtils.isEmpty(num)){
//                return;
//            }
//            //  不为空。
//            int number = Integer.parseInt(num);
//            //  int i = 1/0;
//            this.redisTemplate.opsForValue().set("num", String.valueOf(++number));
//            //  使用守护线程来实现锁的续期：保证当前资源只有一个线程操作.
//            //            Thread thread = new Thread(() -> {
//            //                //  获取到当前的锁的过期时间
//            //                Long ttlCount = this.redisTemplate.getExpire("lock");
//            //                if (ttlCount.longValue()==1){
//            //                    //  在续期3秒
//            //                    this.redisTemplate.expire("lock",3,TimeUnit.SECONDS);
//            //                }
//            //            });
//            //            thread.start();
//            //            thread.setDaemon(true);
//            //  判断 -- 不能限制有多个线程同时操作资源
//            //            while (token.equals(this.redisTemplate.opsForValue().get("lock"))){
//            //                //  index1 要执行删除锁的命令. cpu让出执行权限+因为锁到期了，释放锁。 index2 进来了。
//            //                //  cpu 又将执行权限交个了index1, 此时index1 会执行删除命令，此时删除的是 index2的锁！
//            //                //  删除锁缺乏原子性！
//            //                //  释放
//            //                this.redisTemplate.delete("lock");
//            //            }
//            //  定义一个lua脚本 ---
//            String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//                    "then\n" +
//                    "    return redis.call(\"del\",KEYS[1])\n" +
//                    "else\n" +
//                    "    return 0\n" +
//                    "end";
//
//            //  执行lua脚本：
//            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
//            redisScript.setScriptText(scriptText);
//            redisScript.setResultType(Long.class);
//            //  第一个参数：封装lua 脚本的对象 第二个参数：缓存锁的key
//            Long count = this.redisTemplate.execute(redisScript, Arrays.asList("lock"), token);
//            if (count==0){
//                return;
//            }
//            System.out.println("删除成功:"+count);
//
//        }else {
//            try {
//                Thread.sleep(300);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            testLock();
//        }
//    }
}
